import { BaseComp } from '../../common/component/comp';
import { ZRenderComp } from '../../common/component/zrComp';

/**
 * PathView
 * ├── Background (z-index=0)
 * ├── PathPainter (z-index=0)
 */

class PathView extends BaseComp {
  constructor(args) {
    super(null);
    this.context = args.context;

    /** @type {HTMLDivElement} */ this.dom = args.dom;

    this.zrender = args.zrender;
    const zr = this.zrender.init(this.dom);
    this.defer(() => zr.dispose());

    const width = zr.getWidth();
    const height = zr.getHeight();
    this.canvasSize = [width, height];
    this.originPoint = [width / 2, height / 2];
    this.scale = (width + height) / 100;

    this.bg = new Background({
      parent : this,
      zrender : this.zrender,
      zr,
      originPoint : this.originPoint,
      scale : this.scale,
    });

    this.painter = new PathPainter({
      parent : this,
      zrender : this.zrender,
      zr,
      originPoint : this.originPoint,
      scale : this.scale,
    });
  }

  setPath(path) {
    this.painter.setPath(path);
  }

  setAxis(scale, originX, originY) {
    if (scale == null && originX == null && originY == null) {
      return;
    }
    scale = scale == null ? this.scale : scale;
    originX = originX == null ? this.originPoint[0] : originX;
    originY = originY == null ? this.originPoint[1] : originY;

    if (typeof(scale) != 'number' || scale <= 0) {
      throw { msg : '坐标轴设置错误', value : {scale} };
    }
    if (typeof(originX) != 'number') {
      throw { msg : '坐标轴设置错误', value : {originX} };
    }
    if (typeof(originY) != 'number') {
      throw { msg : '坐标轴设置错误', value : {originY} };
    }

    this.scale = scale;
    this.originPoint = [originX, originY];
    this.emit('view/axis', { scale, originX, originY });
  }
}

class Background extends ZRenderComp {
  constructor(args) {
		super(args.parent, args.zrender);
    this.useExistZRenderInstance(args.zr, 0, 0);

    /** @type {number[]} */ this.originPoint = args.originPoint;
    /** @type {number} */ this.scale = args.scale;

    this.on('view/axis', (_, v) => this.onAxisUpdated(v.scale, v.originX, v.originY));
  }

  init() {
    const { originPoint, scale } = this;
    const [ ox, oy ] = originPoint;
    const canvasW = this.zr.getWidth();
    const canvasH = this.zr.getHeight();

    this.spBlack = this.createRect(canvasW, canvasH)
      .fill('#000')
      .z(0, -10)
      .build();

    // 坐标轴
    this.spAxisX = this.createLine(0, oy, canvasW, oy)
      .stroke('#888', 4)
      .z(0, 10)
      .build();
    this.spAxisY = this.createLine(ox, 0, ox, canvasH)
      .stroke('#888', 4)
      .z(0, 10)
      .build();

    // 网格线 + 刻度
    // 4 种方式: 1 小线, 2 大线 / 5 小线, 10 大线 / 25 小线, 50 大线 / 100 小线, 200 大线

    this.spGrids = [];
    this.spValues = [];
    this.drawAxis(scale, ox, oy);
  }

  onAxisUpdated(scale, ox, oy) {
    this.originPoint[0] = ox;
    this.originPoint[1] = oy;
    this.drawAxis(scale, ox, oy);
  }

  drawAxis(scale, ox, oy) {
    this.scale = scale;
    const canvasW = this.zr.getWidth();
    const canvasH = this.zr.getHeight();
    
    // 坐标轴
    this.spAxisX.lineShape().y1(oy).y2(oy).update();
    this.spAxisY.lineShape().x1(ox).x2(ox).update();

    // 网格线 + 刻度
    // 4 种方式: 1 小线, 2 大线 / 5 小线, 10 大线 / 25 小线, 50 大线 / 100 小线, 200 大线
    const minX = -ox / scale;
    const minY = -oy / scale;
    const maxX = (canvasW - ox) / scale;
    const maxY = (canvasH - oy) / scale;
    const spanXY = (maxX - minX) + (maxY - minY);
    const unit = spanXY < 40 ? 1 : spanXY < 200 ? 5 : spanXY < 800 ? 25 : 100;

    const createSpValue = (text, color, x, y) => {
      return this.createText(text, 14)
        .textFill(color)
        .textStyle('left', 'bottom', '500', 'Consolas')
        .position(x, y)
        .z(0, 11)
        .build()
    }
    const createSpLine = (x1, y1, x2, y2, color, width) => {
      return this.createLine(x1, y1, x2, y2)
        .stroke(color, width)
        .z(0, 9)
        .build()
    }

    this.spGrids.forEach(sp => sp.destroy());
    this.spGrids = [];
    this.spValues.forEach(sp => sp.destroy());
    this.spValues = [];

    let i = 1;
    for (let y = unit; y < maxY; y += unit, i++) {
      let yy = oy + y * scale;
      const { color, width } = this._unitParams(i);
      this.spGrids.push(createSpLine(0, yy, canvasW, yy, color, width));
      this.spValues.push(createSpValue(`${unit*i}`, color, ox + 5, yy - 5));
    }
    i = 1;
    for (let y = -unit; y > minY; y -= unit, i++) {
      let yy = oy + y * scale;
      const { color, width } = this._unitParams(i);
      this.spGrids.push(createSpLine(0, yy, canvasW, yy, color, width));
      this.spValues.push(createSpValue(`-${unit*i}`, color, ox + 5, yy - 5));
    }

    i = 1;
    for (let x = unit; x < maxX; x += unit, i++) {
      let xx = ox + x * scale;
      const { color, width } = this._unitParams(i);
      this.spGrids.push(createSpLine(xx, 0, xx, canvasH, color, width));
      this.spValues.push(createSpValue(`${unit*i}`, color, xx + 5, oy - 5));
    }
    i = 1;
    for (let x = -unit; x > minX; x -= unit, i++) {
      let xx = ox + x * scale;
      const { color, width } = this._unitParams(i);
      this.spGrids.push(createSpLine(xx, 0, xx, canvasH, color, width));
      this.spValues.push(createSpValue(`-${unit*i}`, color, xx + 5, oy - 5));
    }
  }

  /**
   * @param {number} ii 
   */
  _unitParams(ii) {
    return {
      color : ii % 2 == 1 ? '#555' : '#666',
      width : ii % 2 == 1 ? 1 : 2,
    };
  }
}

class PathPainter extends ZRenderComp {
  constructor(args) {
    super(args.parent, args.zrender);
    this.useExistZRenderInstance(args.zr, 0, 0);

    /** @type {number[]} */ this.originPoint = args.originPoint;
    /** @type {number} */ this.scale = args.scale;
    this.path = '';

    this.on('view/axis', (_, v) => {
      this.scale = v.scale;
      this.originPoint[0] = v.originX;
      this.originPoint[1] = v.originY;
      this.onAxisUpdated(v.scale, v.originX, v.originY);
    });
  }

  init() {
    if (this.path) {
      this.drawPath(this.path);
    }
  }

  setPath(path) {
    if (this.sp) {
      this.sp.destroy();
      this.sp2.destroy();
      this.sp = this.sp2 = null;
    }
    this.path = path;
    if (path) {
      this.drawPath(path);
    }
  }

  drawPath(path) {
    this.sp = this.createPathFromString(path)
      .fill('#48B')
      .scale(this.scale)
      .position(this.originPoint[0], this.originPoint[1])
      .build();
    this.sp2 = this.createPathFromString(path)
      .stroke('#FFF', 1 / this.scale)
      .fillNone()
      .scale(this.scale)
      .position(this.originPoint[0], this.originPoint[1])
      .z(0, 100)
      .build();
  }

  onAxisUpdated(scale, ox, oy) {
    this.sp && this.sp.style().scale(scale).position(ox, oy).update();
    this.sp2 && this.sp2.style().scale(scale).stroke('#FFF', 1 / scale).position(ox, oy).update();
  }
}

export {
	PathView
};

